package util;

import annotation.Table;
import annotation.TableField;
import annotation.TableId;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 反射工具类
 *
 * @author caoqianfan
 * @date 2024/7/15 下午3:47
 */
public class ReflectUtils {

    /**
     * 获取实体类主键
     *
     * @param clazz 实体类
     * @return Field
     */
    public static Field getIdField(Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        Field item = null;
        for (Field field : fields) {
            TableId id = field.getAnnotation(TableId.class);
            if (id != null) {
                field.setAccessible(true);
                item = field;
                break;
            }
        }
        if (item == null) {
            Class<?> superclass = clazz.getSuperclass();
            if (superclass != null) {
                item = getIdField(superclass);
            }
        }
        return item;
    }

    /**
     * 根据主键名称获取实体类主键属性值
     *
     * @param clazz  类名
     * @param pkName 主键名
     * @return 属性值
     */
    public static Object getPkValueByName(Object clazz, String pkName) {
        try {
            String firstLetter = pkName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + pkName.substring(1);
            Method method = clazz.getClass().getMethod(getter);
            return method.invoke(clazz);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 通过反射将 class1不为空的值赋值给class2
     *
     * @param class1 class1
     * @param class2 class2
     */
    public static void reflectClass1ToClass2(Object class1, Object class2) throws Exception {
        Field[] field = class1.getClass().getDeclaredFields();
        for (Field item : field) {
            String name = item.getName();
            if ("serialVersionUID".equals(name)) {
                continue;
            }
            name = name.substring(0, 1).toUpperCase() + name.substring(1);
            Method m1 = class1.getClass().getMethod("get" + name);
            Object value = m1.invoke(class1);
            if (value != null) {
                item.setAccessible(true);
                item.set(class2, value);
            }
        }
    }

    /**
     * 获取实体类 @TableId @TableField 的属性名称
     *
     * @param clazz 类名
     * @return {@code Map<String, String>}
     */
    public static Map<String, String> getColumnName(Class<?> clazz) {
        Map<String, String> map = new ConcurrentHashMap<>();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(TableId.class)) {
                TableId declaredAnnotation = field.getAnnotation(TableId.class);
                String columnName = declaredAnnotation.value();
                // 检查注解的 value 是否为空，如果为空，则使用字段名作为列名
                String column = columnName.isEmpty() ? StringUtils.camelToUnderScore(field.getName()) : columnName;
                // 使用字段名作为 map 的键
                map.put(field.getName(), column);
            }
            if (field.isAnnotationPresent(TableField.class)) {
                TableField declaredAnnotation = field.getAnnotation(TableField.class);
                String columnName = declaredAnnotation.value();
                // 检查注解的 value 是否为空，如果为空，则使用字段名作为列名
                String column = columnName.isEmpty() ? StringUtils.camelToUnderScore(field.getName()) : columnName;
                // 使用字段名作为 map 的键
                map.put(field.getName(), column);
            }
        }
        return map;
    }

    /**
     * 通过获取类上的@Table注解获取表名称
     *
     * @param clazz 类名
     * @return {@code Map<String, String>}
     */
    public static Map<String, String> getTableName(Class<?> clazz) {
        Map<String, String> map = new ConcurrentHashMap<>();
        Table annotation = clazz.getAnnotation(Table.class);
        String name = annotation.value();
        String className = clazz.getSimpleName();
        map.put("tableName", name);
        map.put("className", className);
        return map;
    }
}